/*!	 layerparamtreestore.cpp
**	 Template File
**
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**	Copyright (c) 2007, 2008 Chris Moore
**	Copyright (c) 2011 Carlos López
**	Copyright (c) 2012-2013 Konstantin Dmitriev
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <synfig/general.h>

#include <glibmm/main.h>

#include "layerparamtreestore.h"
#include "iconcontroller.h"
#include <gtkmm/button.h>
#include <synfig/paramdesc.h>
#include <synfig/valuenodes/valuenode_bone.h>
#include <synfig/valuenodes/valuenode_composite.h>

#include "layertree.h"
#include <synfigapp/action_system.h>
#include <synfigapp/instance.h>
#include "app.h"
#include <ETL/clock>

#include <gui/localization.h>

#endif

using namespace std;
using namespace etl;
using namespace synfig;
using namespace studio;

class Profiler : private etl::clock
{
    const std::string name;
public:
    Profiler(const std::string& name): name(name)
    {
        reset();
    }
    ~Profiler()
    {
        float time(operator()());
        synfig::info("%s: took %f msec", name.c_str(), time * 1000);
    }
};

static LayerParamTreeStore::Model& ModelHack()
{
    static LayerParamTreeStore::Model* model(0);

    if (!model) {
        model = new LayerParamTreeStore::Model;
    }

    return *model;
}

LayerParamTreeStore::LayerParamTreeStore(etl::loose_handle<synfigapp::CanvasInterface> canvas_interface_, LayerTree* layer_tree):
    Gtk::TreeStore(ModelHack()),
    CanvasTreeStore(canvas_interface_),
    layer_tree(layer_tree)
{
    queued = 0;
    // Connect all the signals
    canvas_interface()->signal_value_node_changed().connect(sigc::mem_fun(*this, &studio::LayerParamTreeStore::on_value_node_changed));
    canvas_interface()->signal_value_node_renamed().connect(sigc::mem_fun(*this, &studio::LayerParamTreeStore::on_value_node_renamed));
    canvas_interface()->signal_value_node_added().connect(sigc::mem_fun(*this, &studio::LayerParamTreeStore::on_value_node_added));
    canvas_interface()->signal_value_node_deleted().connect(sigc::mem_fun(*this, &studio::LayerParamTreeStore::on_value_node_deleted));
    canvas_interface()->signal_value_node_replaced().connect(sigc::mem_fun(*this, &studio::LayerParamTreeStore::on_value_node_replaced));
    canvas_interface()->signal_layer_param_changed().connect(sigc::mem_fun(*this, &studio::LayerParamTreeStore::on_layer_param_changed));

    canvas_interface()->signal_value_node_child_added().connect(sigc::mem_fun(*this, &studio::LayerParamTreeStore::on_value_node_child_added));
    canvas_interface()->signal_value_node_child_removed().connect(sigc::mem_fun(*this, &studio::LayerParamTreeStore::on_value_node_child_removed));

    // This is for updating timetrack when empty/disabled keyframe is moved/deleted
    // Looks a bit hackish, but I don't know the other way to do that. --KD
    canvas_interface()->signal_keyframe_changed().connect(sigc::hide_return(sigc::hide(sigc::mem_fun(*this, &studio::LayerParamTreeStore::queue_refresh))));
    canvas_interface()->signal_keyframe_removed().connect(sigc::hide_return(sigc::hide(sigc::mem_fun(*this, &studio::LayerParamTreeStore::queue_refresh))));

    layer_tree->get_selection()->signal_changed().connect(sigc::mem_fun(*this, &LayerParamTreeStore::queue_rebuild));

    signal_changed().connect(sigc::mem_fun(*this, &LayerParamTreeStore::queue_refresh));
    rebuild();
}

LayerParamTreeStore::~LayerParamTreeStore()
{
    while (!changed_connection_list.empty()) {
        changed_connection_list.back().disconnect();
        changed_connection_list.pop_back();
    }

    if (getenv("SYNFIG_DEBUG_DESTRUCTORS")) {
        synfig::info("LayerParamTreeStore::~LayerParamTreeStore(): Deleted");
    }
}

Glib::RefPtr<LayerParamTreeStore>
LayerParamTreeStore::create(etl::loose_handle<synfigapp::CanvasInterface> canvas_interface_, LayerTree*layer_tree)
{
    return Glib::RefPtr<LayerParamTreeStore>(new LayerParamTreeStore(canvas_interface_, layer_tree));
}

void
LayerParamTreeStore::get_value_vfunc(const Gtk::TreeModel::iterator& iter, int column, Glib::ValueBase& value)const
{
    if (column < 0) {
        synfig::error("LayerParamTreeStore::get_value_vfunc(): Bad column!");
        return;
    }

    if (column == model.label.index()) {
        synfigapp::ValueDesc value_desc((*iter)[model.value_desc]);
        Glib::ustring label;

        if (!(*iter)[model.is_toplevel]) {
            return CanvasTreeStore::get_value_vfunc(iter, column, value);
        }

        synfig::ParamDesc param_desc((*iter)[model.param_desc]);
        label = param_desc.get_local_name();

        if (!(*iter)[model.is_inconsistent])
            if (value_desc.is_value_node() && value_desc.get_value_node()->is_exported()) {
                label += strprintf(" (%s)", value_desc.get_value_node()->get_id().c_str());
            }

        Glib::Value<Glib::ustring> x;
        g_value_init(x.gobj(), x.value_type());

        x.set(label);

        g_value_init(value.gobj(), x.value_type());
        g_value_copy(x.gobj(), value.gobj());
    } else if (column == model.is_toplevel.index()) {
        Glib::Value<bool> x;
        g_value_init(x.gobj(), x.value_type());

        TreeModel::Path path(get_path(iter));

        x.set(path.size() <= 1);

        g_value_init(value.gobj(), x.value_type());
        g_value_copy(x.gobj(), value.gobj());
    } else if (column == model.is_inconsistent.index()) {
        if ((*iter)[model.is_toplevel]) {
            CanvasTreeStore::get_value_vfunc(iter, column, value);
            return;
        }

        Glib::Value<bool> x;
        g_value_init(x.gobj(), x.value_type());

        x.set(false);

        g_value_init(value.gobj(), x.value_type());
        g_value_copy(x.gobj(), value.gobj());
    } else {
        CanvasTreeStore::get_value_vfunc(iter, column, value);
    }
}

void
LayerParamTreeStore::set_value_impl(const Gtk::TreeModel::iterator& iter, int column, const Glib::ValueBase& value)
{

    if (column >= get_n_columns_vfunc()) {
        g_warning("LayerTreeStore::set_value_impl: Bad column (%d)", column);
        return;
    }

    if (!g_value_type_compatible(G_VALUE_TYPE(value.gobj()), get_column_type_vfunc(column))) {
        g_warning("LayerTreeStore::set_value_impl: Bad value type");
        return;
    }

    try {
        if (column == model.value.index()) {
            Glib::Value<synfig::ValueBase> x;
            g_value_init(x.gobj(), model.value.type());
            g_value_copy(value.gobj(), x.gobj());

            if ((bool)(*iter)[model.is_toplevel]) {
                synfigapp::Action::PassiveGrouper group(canvas_interface()->get_instance().get(), _("Set Layer Parameters"));

                synfig::ParamDesc param_desc((*iter)[model.param_desc]);

                LayerList::iterator iter2(layer_list.begin());

                for (; iter2 != layer_list.end(); ++iter2) {
                    if (!canvas_interface()->change_value(synfigapp::ValueDesc(*iter2, param_desc.get_name()), x.get())) {
                        // ERROR!
                        group.cancel();
                        App::dialog_message_1b(
                            "ERROR",
                            _("Unable to set all layer parameters."),
                            "details",
                            _("Close"));

                        return;
                    }
                }
            } else {
                canvas_interface()->change_value((*iter)[model.value_desc], x.get());
            }

            return;
        } else

        {
            CanvasTreeStore::set_value_impl(iter, column, value);
        }
    } catch (std::exception& x) {
        g_warning("%s", x.what());
    }
}

void
LayerParamTreeStore::rebuild()
{
    if (queued) {
        queued = 0;
    }

    clear();
    layer_list = layer_tree->get_selected_layers();

    if (layer_list.size() <= 0) {
        return;
    }

    // Get rid of all the connections,
    // and clear the connection map.
    while (!changed_connection_list.empty()) {
        changed_connection_list.back().disconnect();
        changed_connection_list.pop_back();
    }

    struct REBUILD_HELPER {
        ParamVocab vocab;
        Layer::Handle layer_0;

        static ParamVocab::iterator find_param_desc(ParamVocab& vocab, const synfig::String& x)
        {
            ParamVocab::iterator iter;

            for (iter = vocab.begin(); iter != vocab.end(); ++iter)
                if (iter->get_name() == x) {
                    break;
                }

            return iter;
        }

        void process_vocab(synfig::Layer::Handle layer_n)
        {
            ParamVocab x = layer_n->get_param_vocab();
            ParamVocab::iterator iter;

            for (iter = vocab.begin(); iter != vocab.end(); ++iter) {
                String name(iter->get_name());
                ParamVocab::iterator iter2(find_param_desc(x, name));

                if (iter2 == x.end() ||
                        layer_0->get_param(name).get_type() != layer_n->get_param(name).get_type()) {
                    // remove it and start over
                    vocab.erase(iter);
                    iter = vocab.begin();
                    iter--;
                    continue;
                }
            }
        }

    } rebuild_helper;

    {
        LayerList::iterator iter(layer_list.begin());
        rebuild_helper.vocab = (*iter)->get_param_vocab();
        rebuild_helper.layer_0 = *iter;

        for (++iter; iter != layer_list.end(); ++iter) {
            rebuild_helper.process_vocab(*iter);
            changed_connection_list.push_back(
                (*iter)->signal_changed().connect(
                    sigc::mem_fun(
                        *this,
                        &LayerParamTreeStore::changed
                    )
                )
            );
        }
    }

    ParamVocab::iterator iter;

    for (iter = rebuild_helper.vocab.begin(); iter != rebuild_helper.vocab.end(); ++iter) {
        if (iter->get_hidden()) {
            continue;
        }


        Gtk::TreeRow row(*(append()));
        synfigapp::ValueDesc value_desc(layer_list.front(), iter->get_name());
        CanvasTreeStore::set_row(row, value_desc);

        if (value_desc.is_value_node()) {
            changed_connection_list.push_back(
                value_desc.get_value_node()->signal_changed().connect(
                    sigc::mem_fun(
                        this,
                        &LayerParamTreeStore::changed
                    )
                )
            );
        }

        if (value_desc.get_value_type() == type_canvas) {
            Canvas::Handle canvas_handle = value_desc.get_value().get(Canvas::Handle());

            if (canvas_handle) changed_connection_list.push_back(
                    canvas_handle->signal_changed().connect(
                        sigc::mem_fun(
                            this,
                            &LayerParamTreeStore::changed
                        )
                    )
                );
        }

        // row[model.label] = iter->get_local_name();
        row[model.tooltip] = iter->get_local_name() + ": " + iter->get_description();
        row[model.param_desc] = *iter;
        row[model.canvas] = layer_list.front()->get_canvas();
        row[model.is_inconsistent] = false;


        LayerList::iterator iter2(layer_list.begin());
        ValueBase value((*iter2)->get_param(iter->get_name()));

        for (++iter2; iter2 != layer_list.end(); ++iter2) {
            if (value != ((*iter2)->get_param(iter->get_name()))) {
                row[model.is_inconsistent] = true;

                while (!row.children().empty() && erase(row.children().begin()))
                    ;

                break;
            }
        }
    }
}

void
LayerParamTreeStore::queue_refresh()
{
    if (queued) {
        return;
    }

    queued = 1;
    queue_connection.disconnect();
    queue_connection = Glib::signal_timeout().connect(
                           sigc::bind_return(
                               sigc::mem_fun(*this, &LayerParamTreeStore::refresh),
                               false
                           )
                           , 150);

}

void
LayerParamTreeStore::queue_rebuild()
{
    if (queued == 2) {
        return;
    }

    queued = 2;
    queue_connection.disconnect();
    queue_connection = Glib::signal_timeout().connect(
                           sigc::bind_return(
                               sigc::mem_fun(*this, &LayerParamTreeStore::rebuild),
                               false
                           )
                           , 150);

}

void
LayerParamTreeStore::refresh()
{
    if (queued) {
        queued = 0;
    }

    Gtk::TreeModel::Children children_(children());

    Gtk::TreeModel::Children::iterator iter;

    if (!children_.empty())
        for (iter = children_.begin(); iter && iter != children_.end(); ++iter) {
            Gtk::TreeRow row = *iter;
            refresh_row(row);
        }
}

void
LayerParamTreeStore::refresh_row(Gtk::TreeModel::Row &row)
{
    if (row[model.is_toplevel]) {
        row[model.is_inconsistent] = false;
        ParamDesc param_desc(row[model.param_desc]);

        LayerList::iterator iter2(layer_list.begin());
        ValueBase value((*iter2)->get_param(param_desc.get_name()));

        for (++iter2; iter2 != layer_list.end(); ++iter2) {
            if (value != ((*iter2)->get_param(param_desc.get_name()))) {
                row[model.is_inconsistent] = true;

                while (!row.children().empty() && erase(row.children().begin()))
                    ;

                return;
            }
        }
    }

    {
        CanvasTreeStore::refresh_row(row);
        return;
    }
}

void
LayerParamTreeStore::set_row(Gtk::TreeRow row, synfigapp::ValueDesc value_desc)
{
    Gtk::TreeModel::Children children = row.children();

    while (!children.empty() && erase(children.begin()))
        ;

    CanvasTreeStore::set_row(row, value_desc);
}

void
LayerParamTreeStore::on_value_node_added(synfig::ValueNode::Handle /*value_node*/)
{
}

void
LayerParamTreeStore::on_value_node_deleted(synfig::ValueNode::Handle /*value_node*/)
{
}

void
LayerParamTreeStore::on_value_node_child_added(synfig::ValueNode::Handle /*value_node*/, synfig::ValueNode::Handle /*child*/)
{
    queue_rebuild();
}

void
LayerParamTreeStore::on_value_node_child_removed(synfig::ValueNode::Handle /*value_node*/, synfig::ValueNode::Handle /*child*/)
{
    rebuild();
}

void
LayerParamTreeStore::on_value_node_changed(synfig::ValueNode::Handle /*value_node*/)
{
    queue_refresh();
}

void
LayerParamTreeStore::on_value_node_renamed(synfig::ValueNode::Handle /*value_node*/)
{
    rebuild();
}

void
LayerParamTreeStore::on_value_node_replaced(synfig::ValueNode::Handle /*replaced_value_node*/, synfig::ValueNode::Handle /*new_value_node*/)
{
    // this used to be "queue_refresh();" but it was crashing when we
    // first animated a bone's parent parameter (if the params panel
    // was quite large).  the replaced_value_node handle was out of
    // scope by the time the tree was rebuilt, and the tree code was
    // failing as a result.  not sure how the tree code has a pointer
    // rather than a handle - maybe it has a loosehandle, that would
    // cause the problem I was seeing
    rebuild();
}

void
LayerParamTreeStore::on_layer_param_changed(synfig::Layer::Handle /*handle*/, synfig::String /*param_name*/)
{
    queue_refresh();
}

bool
LayerParamTreeStore::find_value_desc(const synfigapp::ValueDesc& value_desc, Gtk::TreeIter& iter)
{
    // check level0
    for (iter = children().begin(); iter && iter != children().end(); ++iter) {
        if (value_desc.is_value_node()) {
            // transformation handle is at level 0, force to inspect deeper if case.
            if ((value_desc.get_value_node() == ((synfigapp::ValueDesc)(*iter)[model.value_desc]).get_value_node()) &&
                    (value_desc.get_value_type() == type_transformation)
               ) {
                Gtk::TreeIter iter2 = iter->children().begin();

                for (; iter2 && iter2 != iter->children().end(); ++iter2) {
                    if (((synfigapp::ValueDesc)(*iter2)[model.value_desc]).get_name() ==
                            value_desc.get_sub_name()) {
                        iter = iter2;
                        return true;
                    }
                }
            }
        }

        // something found
        if (value_desc == (*iter)[model.value_desc]) {
            return true;
        }
    }

    // let's go for deepness
    Gtk::TreeIter iter2;

    for (iter2 = children().begin(); iter2 && iter2 != children().end(); ++iter2) {
        if ((*iter2).children().empty()) {
            continue;
        }

        if (find_value_desc(value_desc, iter, iter2->children())) {
            return true;
        }
    }

    return false;
}

bool
LayerParamTreeStore::find_value_desc(const synfigapp::ValueDesc& value_desc, Gtk::TreeIter& iter, const Gtk::TreeNodeChildren child_iter)
{
    // actual level from child_iter
    for (iter = child_iter.begin(); iter && iter != child_iter.end(); ++iter) {
        if (value_desc.is_value_node()) {
            // for vertex handle force to inspect deeper
            if (value_desc.get_value_node() == ((synfigapp::ValueDesc)(*iter)[model.value_desc]).get_value_node() &&
                    (value_desc.get_value_type() == type_bline_point)
               )
// seems to be not necessary to check the ValueNode
//                     if((ValueNode_Composite::Handle::cast_dynamic(
//                             )
            {
                // check iteratively spline point (bline) children for same name.
                Gtk::TreeIter iter2 = iter->children().begin();

                for (; iter2 && iter2 != iter->children().end(); ++iter2) {
                    if (((synfigapp::ValueDesc)(*iter2)[model.value_desc]).get_name() ==
                            value_desc.get_sub_name()) {
                        iter = iter2;
                        return true;
                    }
                }
            }
        }

        if (value_desc == (*iter)[model.value_desc]) {
            return true;
        }
    }

    Gtk::TreeIter iter2 = child_iter.begin();

    // for bones, do not inspect recursively to don't get trapped in bones hierarchy
    if (((synfigapp::ValueDesc)(*iter2)[model.value_desc]).parent_is_value_node()) {
        if (ValueNode_Bone::Handle::cast_dynamic(
                    ((synfigapp::ValueDesc)(*iter2)[model.value_desc]).get_parent_value_node())) {
            return false;
        }
    }

    for (; iter2 && iter2 != child_iter.end(); ++iter2) {
        if ((*iter2).children().empty()) {
            continue;
        }

        if (find_value_desc(value_desc, iter, iter2->children())) {
            return true;
        }
    }

    return false;
}